www.gusucode.com > VC++写的C编译器源代码附设计文档-源码程序 > VC++写的C编译器源代码附设计文档-源码程序/code/C- Compiler/Analyzer.cpp

    // Analyzer.cpp : implementation file
// Download by http://www.NewXing.com

#include "stdafx.h"
#include "cminus.h"

#include "Analyzer.h"

/*  *    CAnalyzer
    *    Construction & destruction
  *	* *
   ***   Programer: 陆晓春
    *    Date:		2004.05.20             */

// static member variables initiation
CSymbolTable	CAnalyzer::m_SymbolTable;
CFunArgsCheck	CAnalyzer::m_FunArgs;
int				CAnalyzer::location;

CAnalyzer::CAnalyzer( CString& str )
{
	m_pParser = new CParser( str );
	m_pProgram = NULL;
	m_SymbolTable.deleteHashTable();
	m_FunArgs.deleteList();

	location = 0;
}

CAnalyzer::~CAnalyzer()
{
	if( m_pParser ) delete m_pParser;
	// DO NOT DELETE m_pProgram, it points to the tree in the m_pParser,
	// which has been destructed above
}

/*  *    CAnalyzer
    *    public operations
  *	* *
   ***   Programer: 陆晓春
    *    Date:		2004.05.20             */

// build the symbol table
void CAnalyzer::BuildSymbolTable( CTreeNode* pNode )
{
	traverse( pNode, insertNode, nullProc );
}

// Procedure typeCheck performs type checking by a postorder syntax tree traversal
void CAnalyzer::typeCheck( CTreeNode* pNode )
{
	traverse( pNode, nullProc, checkNode );
	// after semantic analysis, check if main() exists
	if( m_SymbolTable.st_lookup( CString("main"), CString("global") ) == -1 )
		OutputErrMsg( "\r\nUnresolved external symbol _main" );
}

// trace the symbol table
void CAnalyzer::Trace( LPCTSTR lpszPathName )
{
	ClearErrFlag();

	OutputPhaseMsg( "building syntax tree..." );
	m_pProgram = m_pParser->BuildSyntaxTree();
	if( bErrFlag ) {
		OutputPhaseMsg( "\r\nerrors occur while parsing, stop constructing symbol table!" );
		return;
	} else
		OutputPhaseMsg( "successfully build the syntax tree!" );
	OutputPhaseMsg( "\r\nconstructing symbol table..." );
	BuildSymbolTable( m_pProgram );
	// ignore errors
	m_SymbolTable.PrintSymbolTable( lpszPathName );
}

// trace type checking
void CAnalyzer::TraceTypeCheck()
{
	ClearErrFlag();

	OutputPhaseMsg( "building syntax tree..." );
	m_pProgram = m_pParser->BuildSyntaxTree();
	if( bErrFlag ) {
		OutputPhaseMsg( "\r\nerrors occur while parsing, stop constructing symbol table!" );
		return;
	}
	OutputPhaseMsg( "\r\nconstructing symbol table..." );
	BuildSymbolTable( m_pProgram );
	if( bErrFlag ) {
		OutputPhaseMsg( "\r\nerrors occur while constructing symbol table, stop type checking!" );
		return;
	}
	OutputPhaseMsg( "\r\ntype checking..." );
	typeCheck( m_pProgram );
	if( bErrFlag )
		OutputPhaseMsg( "\r\nerrors occur while type checking!" );
	else 
		OutputPhaseMsg( "All OK!" );
}

/*  *    CAnalyzer
    *    help routines
  *	* *
   ***   Programer: 陆晓春
    *    Date:		2004.05.20             */
 
// Procedure traverse is a generic recursive syntax tree traversal routine:
// it applies preProc in preorder and postProc in postorder to tree pointed to by t
void CAnalyzer::traverse( CTreeNode* t, 
						  void(* preProc)(CTreeNode*), void(* postProc)(CTreeNode*) )
{
	if( t ) {
		preProc( t );
		for( int i = 0; i < MAX_CHILDREN; i++ )
			traverse( t->child[i], preProc, postProc );
		postProc( t );
		traverse( t->sibling, preProc, postProc );
	}
}

// nullProc is a do-nothing procedure to generate preorder-only or
// postorder-only traversals from traverse
void CAnalyzer::nullProc( CTreeNode* t )
{
	return;
}

// Procedure insertNode inserts identifiers stored in t into 
// the symbol table 
void CAnalyzer::insertNode( CTreeNode* t )
{
	ASSERT( t );
	switch( t->nodekind ) {
	case kFunDec:
		if( m_SymbolTable.st_lookup( t->szName, t->szScope ) == -1 ) {
			// not defined, so add it to the symbol table
			m_SymbolTable.st_insert( t->szName, t->szScope, t->type, t->lineno, location++ );
			// add it to function declaration list
			m_FunArgs.fa_insert( t );
		} else // redefinition
			OutputErrMsg( "error in line %d: function '%s()' redefinition", t->lineno, (LPCTSTR)t->szName );
		break;
	case kVarDec:
	case kParam:
		if( m_SymbolTable.st_lookup( t->szName, t->szScope ) == -1 )
			// not defined, so add it to the symbol table
			m_SymbolTable.st_insert( t->szName, t->szScope, t->type, t->lineno, location++, t->bArray );
		else // redefinition
			OutputErrMsg( "error in line %d: variable '%s' redefinition", t->lineno, (LPCTSTR)t->szName );
		break;
	case kStmt:
		switch( t->kind.stmt ) {
		case kLabel:
			if( m_SymbolTable.st_lookup( t->szName, t->szScope ) == -1 )
				// first time encountered in the scope, add it to the symbol table
				m_SymbolTable.st_insert( t->szName, t->szScope, _LABEL, t->lineno, location++ );
			else // label redifition
				OutputErrMsg( "error in line %d: lable '%s' in scope '%s' has already defined", 
							  t->lineno, (LPCTSTR)t->szName, (LPCTSTR)t->szScope );
			break;
		case kGoto:
			if( m_SymbolTable.st_lookup( t->szName, t->szScope ) == -1 )
				// label undeclared
				OutputErrMsg( "error in line %d: lable '%s' in scope '%s' undeclared",
							  t->lineno, (LPCTSTR)t->szName, (LPCTSTR)t->szScope );
			else
				m_SymbolTable.st_insert( t->szName, t->szScope, _LABEL, t->lineno, 0 );
			break;
		case kCall:
			if( m_SymbolTable.st_lookup( t->szName, t->szScope ) == -1 )
				OutputErrMsg( "error in line %d: unresolved external symbol %s", t->lineno, t->szName );
			else
				m_SymbolTable.st_insert( t->szName, t->szScope, _ID, t->lineno, 0 );
		default:
			break;
		}
		break;
	case kExp:
		switch( t->kind.exp ) {
		case kID:
			if( m_SymbolTable.st_lookup( t->szName, t->szScope ) == -1 &&
				m_SymbolTable.st_lookup( t->szName, CString("global") ) == -1 )
				// undeclared
				OutputErrMsg( "error in line %d: '%s': undeclared identifier", t->lineno, (LPCTSTR)t->szName );
			else if( m_SymbolTable.st_lookup( t->szName, t->szScope ) != -1 ) {
				// local variable
				if( t->father && (t->father->nodekind != kStmt || t->father->kind.stmt != kCall)/* not in call statement */ &&
					t->bArray != m_SymbolTable.st_lookup_isarray(t->szName, t->szScope) ) {
					// one is array but the other is not
					OutputErrMsg( "error in line %d: '%s' is%sdeclared as array", t->lineno,
								  (LPCTSTR)t->szName, t->bArray ? " not " : " " );
					break;
				}
				m_SymbolTable.st_insert( t->szName, t->szScope, t->type, t->lineno, 0 );
			} else { // global variable
				t->szScope = _T("global");
				if( t->father && (t->father->nodekind != kStmt || t->father->kind.stmt != kCall)/* not in call statement */ &&
					t->bArray != m_SymbolTable.st_lookup_isarray(t->szName, t->szScope) ) {
					// one is array but the other is not
					OutputErrMsg( "error in line %d: '%s' is%sdeclared as array", t->lineno,
								  (LPCTSTR)t->szName, t->bArray ? " not " : " " );
					break;
				}
				m_SymbolTable.st_insert( t->szName, t->szScope, t->type, t->lineno, 0 );
			}
			break;
		default:
			break;
		}
	default:
		break;
	}
}

// Procedure checkNode performs type checking at a single tree node
void CAnalyzer::checkNode( CTreeNode* t )
{
	CTreeNode* p = t;
	int ret;

	switch( t->nodekind ) {
	case kStmt:
		switch( t->kind.stmt ) {
		case kRead:
			if( t->child[0] == NULL ) {
				OutputErrMsg( "fatal error: compiler error!" );
				break;
			}
			t->type = t->child[0]->type;
			if( t->type != _CHAR && t->type != _INT && t->type != _FLOAT )
				OutputErrMsg( "error in line %d: read a character, int or float number", t->lineno );
			break;
		case kWrite:
			if( t->child[0] == NULL ) {
				OutputErrMsg( "fatal error: compiler error!" ); break;
			}
			t->type = t->child[0]->type;
			if( t->type != _CHAR && t->type != _INT && t->type != _FLOAT )
				OutputErrMsg( "error in line %d: read a character, int or float number", t->lineno );
			break;
		case kReturn:
			if( t->child[0] == NULL ) { // 'return' returns 'void'
				if( m_SymbolTable.st_lookup_type(t->szName, CString("global")) != _VOID )
					OutputErrMsg( "error in line %d: function '%s' must return a value", 
					              t->lineno, (LPCTSTR)t->szName );
			}
			break;
		case kBreak:
			while( p->father && (p->father->nodekind != kStmt || p->father->kind.stmt != kWhile) )
				p = p->father;
			if( p->father == NULL )
				// 'break' is not within a while statment
				OutputErrMsg( "error in line %d: illegal 'break'", t->lineno );
			break;
		case kContinue: // treat it like kBreak
			while( p->father && (p->father->nodekind != kStmt || p->father->kind.stmt != kWhile) )
				p = p->father;
			if( p->father == NULL )
				// 'continue' is not within a while statment
				OutputErrMsg( "error in line %d: illegal 'continue'", t->lineno );
			break;
		case kCall:
			// check if its arguments match declaration
			ret = m_FunArgs.fa_check( t );
			if( ret != -3 ) {// errors 
				if( ret >= 0 )
					OutputErrMsg( "error in line %d: function '%s()' takes %d parameters", t->lineno, (LPCTSTR)t->szName, ret );
				else if( ret == -1 )
					OutputErrMsg( "error in line %d: function '%s()' is not declared", t->lineno, (LPCTSTR)t->szName );
				else
					OutputErrMsg( "error in line %d: arguments type not match with function '%s()' declaration", t->lineno, (LPCTSTR)t->szName );
				break;
			}
			t->type = m_SymbolTable.st_lookup_type( t->szName, t->szScope );
			break;
		default:
			break;
		}
		break;
	case kExp:
		switch( t->kind.exp ) {
		case kOp:// assign a type to this op node
			if( t->type == LOGICAL_NOT || t->type == ASSIGN )
				t->type = t->child[0]->type;
			else {
				// the others are binary operations, 
				// and the type should be the subexpression with higher precision
				if( t->child[0]->type == _VOID || t->child[1]->type == _VOID )
					OutputErrMsg( "error in line %d: illegal use of type 'void'", t->lineno );
				else if( t->child[0]->type == _FLOAT || t->child[1]->type == _FLOAT )
					t->type = _FLOAT;
				else if( t->child[0]->type == _INT || t->child[1]->type == _INT )
					t->type = _INT;
				else
					t->type = _CHAR;
			}
			break;
		case kID:
			// find the type in the symbol table, assign it to the node
			t->type = m_SymbolTable.st_lookup_type( t->szName, t->szScope );
			t->bArray = m_SymbolTable.st_lookup_isarray( t->szName, t->szScope );
			break;
		default:
			break;
		}
	default:
		break;
	}
}